package cn.jinjimi.app.player.impl;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.ExtractorMediaSource.EventListener;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Predicate;
import cn.jinjimi.domain.data.Cache;
import cn.jinjimi.app.player.OkHttpDataSource;
import cn.jinjimi.app.player.XrkDefaultLoadControl;
import cn.jinjimi.app.player.bean.XrkMusicMedia;

import java.io.File;
import java.io.IOException;

import okhttp3.OkHttpClient;

/**
 * Created by lforxeverc on 2017/5/15.
 */

class XrkExoplayer extends XrkBasePlayer<XrkMusicMedia> {
    private static final String TAG = "XrkExoplayer";
    private static final int MESSAGE_UPDATE_PROGRESS = 1;
    private static final int MESSAGE_UPDATE_BUFFER = 2;
    private static final int MAX_CACHE_SIZE = 100 * 1024 * 1024;

    private SimpleExoPlayer mExoPlayer;
    private TrackSelector mTrackSelector;
    private DataSource.Factory dataSourceFactory;
    private ExtractorsFactory extFactory;
    private DefaultRenderersFactory mRenderFactory;
    private ExoPlayer.EventListener mListener;
    private EventListener mMediaListener;
    private XrkDefaultLoadControl mXrkLoadControl;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MESSAGE_UPDATE_PROGRESS:
                    updateProgress();
                    break;
                case MESSAGE_UPDATE_BUFFER:
                    updateBuffer();
                    break;
            }

        }
    };

    public XrkExoplayer(final Context ctx) {
        setErrorRetry(new DefaultErrorRetry<>(this));
        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        TrackSelection.Factory videoTrackSelectionFactory =
                new AdaptiveTrackSelection.Factory(bandwidthMeter);

        mTrackSelector =
                new DefaultTrackSelector(videoTrackSelectionFactory);

        mRenderFactory = new DefaultRenderersFactory(ctx);

        dataSourceFactory = new DataSource.Factory() {
            @Override
            public DataSource createDataSource() {
                File cacheFile = new File(Cache.getExoMusicCachePath());
                LeastRecentlyUsedCacheEvictor evictor = new LeastRecentlyUsedCacheEvictor(MAX_CACHE_SIZE);
                SimpleCache simpleCache = new SimpleCache(cacheFile, evictor);
                DataSource d = new OkHttpDataSource(new OkHttpClient(), "", new Predicate<String>() {
                    @Override
                    public boolean evaluate(String input) {
                        return true;
                    }
                });
                return new CacheDataSource(simpleCache, d, CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
            }
        };
        mMediaListener = new EventListener() {
            @Override
            public void onLoadError(IOException error) {
                onError(error);
            }
        };
        extFactory = new DefaultExtractorsFactory();
        mListener = new ExoPlayer.EventListener() {
            @Override
            public void onTimelineChanged(Timeline timeline, Object manifest) {
            }

            @Override
            public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

            }

            @Override
            public void onLoadingChanged(boolean isLoading) {
            }

            @Override
            public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
                mHandler.sendEmptyMessage(MESSAGE_UPDATE_PROGRESS);
                mHandler.sendEmptyMessage(MESSAGE_UPDATE_BUFFER);

            }

            @Override
            public void onPlayerError(ExoPlaybackException error) {
                onError(error);
            }

            @Override
            public void onPositionDiscontinuity() {
            }

            @Override
            public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
            }
        };

        mXrkLoadControl = new XrkDefaultLoadControl();
    }

    @Override
    public void start() {
        isPlaying = true;
        if (mExoPlayer != null) {
            mExoPlayer.setPlayWhenReady(true);
        }
    }

    @Override
    public void stop() {
        isPlaying = false;
        if (mExoPlayer != null) {
            mExoPlayer.stop();
            long duration = mExoPlayer.getDuration();
            if (duration != 0) {
                mCurrentPosition = duration;
                if (mUpdateListener != null)
                    mUpdateListener.onUpdate(mMedia, duration, duration);
                if (mCompleteListener != null)
                    mCompleteListener.OnCompletion(mMedia);
            }
        }
    }

    @Override
    public void pause() {
        isPlaying = false;
        if (mExoPlayer != null) {
            mExoPlayer.setPlayWhenReady(false);
        }

    }

    @Override
    public void seekTo(int toPosition) {
        mCurrentPosition = toPosition;
        if (mExoPlayer != null) {
            mExoPlayer.seekTo(toPosition);
            mHandler.sendEmptyMessage(MESSAGE_UPDATE_PROGRESS);
            mHandler.sendEmptyMessage(MESSAGE_UPDATE_BUFFER);
        }

    }

    @Override
    public void release() {
        isPlaying = false;
        if (mExoPlayer != null) {
            mExoPlayer.removeListener(mListener);
            mExoPlayer.release();
            mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS);
            mHandler.removeMessages(MESSAGE_UPDATE_BUFFER);
        }
    }

    @Override
    protected void prepare(XrkMusicMedia music) {
        if (mMedia != null) {
            try {
                if (mExoPlayer != null) {
                    release();
                    mExoPlayer = null;
                }
                mExoPlayer = ExoPlayerFactory.newSimpleInstance(mRenderFactory, mTrackSelector, mXrkLoadControl);

                MediaSource source = new ExtractorMediaSource(mMedia.getUri(), dataSourceFactory, extFactory, null, mMediaListener);
                mExoPlayer.prepare(source);
                mExoPlayer.addListener(mListener);

            } catch (Exception e) {
                onError(e);
            }
        } else {
            Log.e(TAG, "prepare: unsupport media source");
        }
    }


    private void updateProgress() {
        long position = mExoPlayer.getCurrentPosition();
        long duration = mExoPlayer.getDuration();
        duration = duration > 0 ? duration : 0;

        mHandler.removeMessages(MESSAGE_UPDATE_PROGRESS);
        int playbackState = mExoPlayer == null ? ExoPlayer.STATE_IDLE : mExoPlayer.getPlaybackState();
        if (playbackState != ExoPlayer.STATE_IDLE && playbackState != ExoPlayer.STATE_ENDED) {
            long delayMs;
            if (mExoPlayer.getPlayWhenReady() && playbackState == ExoPlayer.STATE_READY) {
                delayMs = 1000 - (position % 1000);
                if (delayMs < 200) {
                    delayMs += 1000;
                }
            } else {
                delayMs = 1000;
            }
            mCurrentPosition = parseTime(position);
            if (mUpdateListener != null && duration >= mCurrentPosition) {
                mUpdateListener.onUpdate(mMedia, duration, mCurrentPosition);
            }
            mHandler.sendEmptyMessageDelayed(MESSAGE_UPDATE_PROGRESS, delayMs);
        }
        if (playbackState == ExoPlayer.STATE_ENDED) {

            if (mCompleteListener != null)
                mCompleteListener.OnCompletion(mMedia);
        }
    }

    private void updateBuffer() {
        mHandler.removeMessages(MESSAGE_UPDATE_BUFFER);
        if (mBufferListener != null && mXrkLoadControl.showBuffering() && mExoPlayer != null) {
            long bfTs = mExoPlayer.getBufferedPosition();
            long whole = mExoPlayer.getDuration();
            mBufferListener.onBuffering(bfTs);
            if (bfTs != whole && whole != 0 && isPlaying) {
                mHandler.sendEmptyMessageDelayed(MESSAGE_UPDATE_BUFFER, 1000);
            }
        }
    }

    private long parseTime(long current) {
        double f = ((double) current) / 1000;
        return Math.round(f) * 1000;
    }

    @Override
    protected boolean retry() {
        if (mExoPlayer != null && mMedia != null) {
            setMediaData(mMedia, false);
            start();
            seekTo((int) mCurrentPosition);
            return true;
        }
        return super.retry();
    }

    @Override
    public void stopBuffering() {
        if (mXrkLoadControl != null)
            mXrkLoadControl.stopLoadBuffer();
    }

    @Override
    public void startBuffering() {
        if (mXrkLoadControl != null)
            mXrkLoadControl.startLoadBuffer();
    }
}